package sg.edu.nus.iss.vmcs.customer;

import java.awt.Frame;
import java.util.Hashtable;

import sg.edu.nus.iss.vmcs.builder.CustomerUIBuilder;
import sg.edu.nus.iss.vmcs.builder.UIDirector;
import sg.edu.nus.iss.vmcs.command.Command;
import sg.edu.nus.iss.vmcs.command.DispenseDrinkCmd;
import sg.edu.nus.iss.vmcs.command.GiveChangeCmd;
import sg.edu.nus.iss.vmcs.command.StoreCashCmd;
import sg.edu.nus.iss.vmcs.store.DrinksBrand;
import sg.edu.nus.iss.vmcs.store.DrinksStore;
import sg.edu.nus.iss.vmcs.store.DrinksStoreItem;
import sg.edu.nus.iss.vmcs.store.Store;
import sg.edu.nus.iss.vmcs.store.StoreController;
import sg.edu.nus.iss.vmcs.system.MainController;
import sg.edu.nus.iss.vmcs.system.SimulatorControlPanel;
import sg.edu.nus.iss.vmcs.util.VMCSException;

/**
 * 
 * @version 1.1
 * @author Yeo Hee Ling, Qu Hui
 * 
 */
public class TransactionController {

	private Boolean changeGiven;
	private Boolean drinkDispensed;
	private Integer price;
	private Integer selection;

	private MainController mCtrl;
	private DispenseController dispenseCtrl;
	private ChangeGiver changeGiver;
	private CoinReceiver coinReceiver;
	private CustomerPanel cPanel;

	// private PropertyLoader drinksLoader;

	private Hashtable commandObjects = new Hashtable();

	public void addCommand(String cmdStr, Command cmd) {
		commandObjects.put(cmdStr, cmd);
	}

	public void dispatch(String cmdStr, Object o) throws VMCSException {
		Command cmd = (Command) commandObjects.get(cmdStr);
		cmd.execute(o);
	}

	public TransactionController(MainController mctrl) {
		mCtrl = mctrl;

		this.dispenseCtrl = new DispenseController(this);
		// Initialize Dispense Drink Command
		DispenseDrinkCmd dispenseDrinkCmd = new DispenseDrinkCmd(dispenseCtrl);
		this.addCommand("DispenseDrink", dispenseDrinkCmd);

		this.changeGiver = new ChangeGiver(this);
		// Initialize Give Change Command
		GiveChangeCmd giveChangeCmd = new GiveChangeCmd(changeGiver);
		this.addCommand("GiveChange", giveChangeCmd);

		this.coinReceiver = new CoinReceiver(this);
		// Initialize Store Cash Command
		StoreCashCmd storeCashCmd = new StoreCashCmd(coinReceiver);
		this.addCommand("ReceiveCoin", storeCashCmd);

		selection = -1;
	}

	public MainController getMainController() {
		return mCtrl;
	}

	public DispenseController getDispenseController() {
		return dispenseCtrl;
	}

	public ChangeGiver getChangeGiver() {
		return changeGiver;
	}

	public CoinReceiver getCoinReceiver() {
		return coinReceiver;
	}

	public CustomerPanel getCustomerPanel() {
		return cPanel;
	}

	public void startTransaction(Integer identifier) {
		selection = identifier;
		StoreController storeCtrl = mCtrl.getStoreController();
		DrinksStore ds = (DrinksStore) storeCtrl.getStore(Store.DRINK);
		DrinksStoreItem dstoreItem = (DrinksStoreItem) ds
				.getStoreItem(identifier);
		DrinksBrand dBrand = (DrinksBrand) dstoreItem.getContent();
		this.price = dBrand.getPrice();
		changeGiver.resetChange();
		dispenseCtrl.resetCan();
		changeGiver.displayChangeStatus();
		dispenseCtrl.allowSelection(false);
		coinReceiver.startReceive();
	}

	public void processMoneyReceived(Integer total) {

		if (total >= this.price) {
			this.completeTransaction();
		} else {
			this.coinReceiver.continueReceive();
		}
	}

	public void completeTransaction() {

		// Dispense Drink
		try {
			// drinkDispensed = dispenseCtrl.dispenseDrink(this.selection);
			String cmdStr = "DispenseDrink";
			TransactionController disp = dispenseCtrl
					.getTransactionController();
			disp.dispatch(cmdStr, this.selection);
			drinkDispensed = true;
		} catch (VMCSException v) {
			drinkDispensed = false;
			System.out.println(v.getMessage());
		}
		if (!drinkDispensed) {
			this.terminateFault();
			return;
		}

		// Give Change
		Integer amount_change = coinReceiver.getTotalInserted() - this.price;
		if (amount_change > 0) {
			// changeGiven = changeGiver.giveChange(amount_change);
			String cmdStr = "GiveChange";
			TransactionController disp = changeGiver.getTransactionController();
			try {
				disp.dispatch(cmdStr, amount_change);
				GiveChangeCmd giveChangeCmd = (GiveChangeCmd) commandObjects
						.get(cmdStr);
				changeGiven = giveChangeCmd.getChangeGiven();
			} catch (VMCSException e) {
				changeGiven = false;
				System.out.println(e.getMessage());
			}
			if (!changeGiven) {
				this.terminateFault();
				return;
			}
		}

		// Store Cash
		Boolean cashStored;
		cashStored = coinReceiver.storeCash();
		if (!cashStored) {
			this.terminateFault();
			return;
		}

		// Update Customer Panel to allow drink selection
		dispenseCtrl.allowSelection(true);

		selection = -1;
	}

	public void terminateFault() {
		this.dispenseCtrl.allowSelection(false);

		if (!this.changeGiven)
			this.coinReceiver.storeCash();
		if (!this.drinkDispensed)
			this.coinReceiver.refundCash();

		selection = -1;
	}

	public void terminateTransaction() {

		if (this.selection >= 0) {
			coinReceiver.stopReceive();
			coinReceiver.refundCash();
		} else {
			dispenseCtrl.allowSelection(false);
		}

		selection = -1;
	}

	public void cancelTransaction() {
		if (selection >= 0) {
			this.coinReceiver.stopReceive();
			this.coinReceiver.refundCash();
			this.dispenseCtrl.allowSelection(true);
		}

		selection = -1;
	}

	public void closeDown() {
		if (cPanel == null)// included by vinod
			return;
		cPanel.dispose();
		mCtrl.getSimulatorControlPanel().setActive(
				SimulatorControlPanel.ACT_CUSTOMER, true);// included by vinod
	}

	public void displayCustomerPanel() {
		SimulatorControlPanel scp = mCtrl.getSimulatorControlPanel();

		// Modified by HeeLing(23/12/2011): Modified to implement builder
		// pattern.
		if (cPanel == null) {
			CustomerUIBuilder custUIBuilder = new CustomerUIBuilder(
					(Frame) scp, this);
			UIDirector uiDirector = new UIDirector(custUIBuilder);
			uiDirector.construct();
			cPanel = custUIBuilder.getResultPanel();
		}
		// End modified by HeeLing(23/12/2011).

		cPanel.display();
		this.getDispenseController().updateDrinkPanel();
		this.getDispenseController().allowSelection(true);
		this.getChangeGiver().displayChangeStatus();
		this.getCoinReceiver().setActive(false);
		// changes outside design vinod
		this.getCustomerPanel().getInvalidCoinDisplay().setState(false);
		this.getCustomerPanel().getNoChangeDisplay().setState(false);
	}

	public void refreshCustomerPanel() {

		// Update Drink Panel
		dispenseCtrl.updateDrinkPanel();
		//
		mCtrl.getSimulationController().getSimulatorControlPanel()
				.setActive(SimulatorControlPanel.ACT_CUSTOMER, true);

		// Allow Customer Transaction
		this.dispenseCtrl.allowSelection(true);

		// Display Change Status
		changeGiver.displayChangeStatus();
	}

}
